package model.ds;

import java.util.ArrayList;


import model.HexDirection;
import model.PlayerColor;
import model.ds.piece.*;
import common.Tuple;


public class GameBoard implements GameBoardInterface{

	private PlayerColor current;
	public Piece[] pieces=new Piece[22];
	public GameTile[] positions = new GameTile[22];
	public ArrayList<PieceType> blackstorage;
	public ArrayList<PieceType> whitestorage;
	private int wqueen=-1;
	private int bqueen=-1;
	private GameTile anchor; 
	private GameTile[][] board;
	
	/**
	 * the default constructor
	 *
	 */
	public GameBoard(){
		this.initializeBlackStorage();
		this.initializeWhiteStorage();
		this.initializeBoard();
		current = PlayerColor.White;
		
		
	}
	
	public void initializeWhiteStorage() {
		whitestorage = new ArrayList<PieceType>();
		whitestorage.add(PieceType.Queen);
		whitestorage.add(PieceType.Bug);
		whitestorage.add(PieceType.Bug);
		whitestorage.add(PieceType.Spider);
		whitestorage.add(PieceType.Spider);
		whitestorage.add(PieceType.Ant);
		whitestorage.add(PieceType.Ant);
		whitestorage.add(PieceType.Ant);
		whitestorage.add(PieceType.GrassHopper);
		whitestorage.add(PieceType.GrassHopper);
		whitestorage.add(PieceType.GrassHopper);
				
	}

	public void initializeBlackStorage() {
		blackstorage = new ArrayList<PieceType>();
		blackstorage.add(PieceType.Queen);
		blackstorage.add(PieceType.Bug);
		blackstorage.add(PieceType.Bug);
		blackstorage.add(PieceType.Spider);
		blackstorage.add(PieceType.Spider);
		blackstorage.add(PieceType.Ant);
		blackstorage.add(PieceType.Ant);
		blackstorage.add(PieceType.Ant);
		blackstorage.add(PieceType.GrassHopper);
		blackstorage.add(PieceType.GrassHopper);
		blackstorage.add(PieceType.GrassHopper);

	}

	/**
	 * the main method to initialize the board as a 3d model, the board hasn`t any borders
	 */
	private void initializeBoard() {
		GameTile[][] board = new GameTile[22][22];
		for(int i=0;i<board.length;i++){
			for(int j=0;j<board.length;j++){
				board[i][j]=new GameTile();
			}
		}
		int oben, unten, links, rechts;
		for(int i=0;i<board.length;i++){
			for(int j=0;j<board.length;j++){
				links=((i==0) ? 21 : i-1);
				rechts=((i==21) ? 0 : i+1);
				oben=((j==0) ? 21 : j-1);
				unten=((j==21) ? 0 : j+1);
				
				
				if(((i<21) && (j<21)) && ((i>0) && (j>0))){
					if(i%2==0){
						board[i][j].upper=board[i][oben];
						board[i][j].upperright=board[rechts][oben];
						board[i][j].upperleft=board[links][oben];
						board[i][j].lower=board[i][unten];
						board[i][j].lowerright=board[rechts][j];
						board[i][j].lowerleft=board[links][j];
					}else{
						board[i][j].upper=board[i][oben];
						board[i][j].upperright=board[rechts][j];
						board[i][j].upperleft=board[links][j];
						board[i][j].lower=board[i][unten];
						board[i][j].lowerright=board[rechts][unten];
						board[i][j].lowerleft=board[links][unten];
						
					}
				}
				
				
			}
		}
		// TODO Auto-generated method stub
		anchor = board[10][10];
	}
	
	
	
	
	/**
	 * the main method to initialize the board as a 3d model, the board hasn`t any borders
	 */
	
	private void initializeBoard2() {
		GameTile[][] board = new GameTile[22][22];
		for(int i=0;i<board.length;i++){
			for(int j=0;j<board.length;j++){
				board[i][j]=new GameTile();
			}
		}
		
		for(int i=0;i<board.length;i++){
			for(int j=0;j<board.length;j++){
				if(((i<21) && (j<21)) && ((i>0) && (j>0))){
					if(i%2==0){
						board[i][j].upper=board[i][j-1];
						board[i][j].upperright=board[i+1][j-1];
						board[i][j].upperleft=board[i-1][j-1];
						board[i][j].lower=board[i][j+1];
						board[i][j].lowerright=board[i+1][j];
						board[i][j].lowerleft=board[i-1][j];
					}else{
						board[i][j].upper=board[i][j-1];
						board[i][j].upperright=board[i+1][j];
						board[i][j].upperleft=board[i-1][j];
						board[i][j].lower=board[i][j+1];
						board[i][j].lowerright=board[i+1][j+1];
						board[i][j].lowerleft=board[i-1][j+1];
						
					}
				}
				
				
			}
		}
		// TODO Auto-generated method stub
		anchor = board[10][10];
	}



	/**
	 * Returns the current player.
	 * @return the current player
	 */
	public PlayerColor getCurrentPlayer() {
		
		return current;
	}

	/**
	 * Returns a list of fields that are reachable. They are defined as tuples of
	 * anchor fields and directions.
	 * @return a list of anchor fields and directions
	 */
	public ArrayList<Tuple<Integer, HexDirection>> getIntroduceable() {
		ArrayList<Tuple<Integer, HexDirection>> list = new ArrayList();
		if(getHighestID()==0){
		
			HexDirection dir = HexDirection.Upper;
			for(int i=0;i<6;i++){
				dir=dir.next();
				list.add(new Tuple(0, dir));
			}
			System.out.println("Zweite runde beginnt" );
		}
		
		else{
	//		
			
			HexDirection dir = HexDirection.Upper;
			HexDirection dir2 = HexDirection.Upper;
			// System.out.println(getHighestID());
			for(int i=0;i<getHighestID()+1;i++){
				if(positions[i].getColor()==current){
					for(int j=0;j<6;j++){
						
						if(!positions[i].isOccupied(dir)){
							boolean var = true;
							for(int v=0;v<6;v++){
								
								if(positions[i].getNeighbor(dir).isOccupied(dir2) && positions[i].getNeighbor(dir).getNeighbor(dir2).getColor()!=current){
									var = false;
								}
								dir2=dir2.next();
							}
							if(var){
								list.add(new Tuple(i, dir));
							}
						}
						dir=dir.next();
					}
				}
			}
			
		}
		return list;
	}

	/**
	 * Returns the number of already introduced pieces.
	 * @return the number of already introduced pieces
	 */
	public int getIntroduced() {
		int i;
		for (i=0; i<22; i++){
			if(pieces[i]==null) break;
		}
		
		return i;
	
	}

	/**
	 * Returns the number of pieces left for a specified player and a given type.
	 * @param player the player
	 * @param pieceType the piece type
	 * @return the number of pieces left for a specified player and a given type
	 */
	public int getLeftPiece(PlayerColor player, PieceType pieceType) {
		int k=0;
		if(player==PlayerColor.Black){
			for(int i=0; i<blackstorage.size(); i++){
				if(blackstorage.get(i)==pieceType )
					k++;
			}
			
		}else{
			for(int i=0; i<whitestorage.size(); i++){
				if(whitestorage.get(i)==pieceType )
					k++;
			}
			
		}
		return k;
	}

	/**
	 * Returns the piece type for a given piece identified by its index.
	 * @param i the index of the piece
	 * @return the type of the piece
	 */
	public PieceType getPieceType(int i) {
		return pieces[i].getType();
		
	}

	/**
	 * Returns the winner of the game, if it is ended already and is no draw.
	 * @return the winner of the game
	 * @throws IllegalStateException if the game has not finished yet or ended with a draw
	 */
	public PlayerColor getWinner() throws IllegalStateException {
		if(!isGameOver()) throw new IllegalStateException("Das Spiel ist noch nicht zu Ende.");
		if(!isDraw()) throw new IllegalStateException("Gleichstand, also kein Gewinner.");
		if(isSurrounded(positions[wqueen])) return PlayerColor.Black;
		if(isSurrounded(positions[bqueen])) return PlayerColor.White;
				
		return null;
	}

	/**
	 * Introduces the first piece to the board. This piece is from the white
	 * player.
	 * @param pieceType the type of the piece
	 * @throws IllegalStateException if another piece has already been set
	 * @throws IllegalArgumentException if the first piece is the queen
	 */
	public void introduce(PieceType pieceType) throws IllegalStateException, IllegalArgumentException {
		if(pieces[0]!=null)throw new IllegalStateException("Es wurde bereits ein Spielstein gelegt.");
		if(pieceType==PieceType.Queen)throw new IllegalArgumentException("Die Queen darf nicht als erste gespielt werden.");
		
		
		switch( pieceType ) {
			case Ant:
				anchor.add(new Ant(current, 0));
				pieces[0]=new Ant(PlayerColor.White, 0);
				break;
			case GrassHopper:
				anchor.add(new GrassHopper(current, 0));
				pieces[0]=new GrassHopper(PlayerColor.White, 0);
				break;
			case Bug:
				anchor.add(new Bug(current, 0));
				pieces[0]=new Bug(PlayerColor.White, 0);
				break;
			case Spider:
				anchor.add(new Spider(current, 0));
				pieces[0]=new Spider(PlayerColor.White, 0);
				break;
		}
		positions[0]=anchor;
		
	//	int i;
	//	for(i=0; i<whitestorage.size(); i++){
	//		if(whitestorage.get(i)==pieceType )
	//			break;
	//	}
		

		int index = whitestorage.indexOf(pieceType);
		whitestorage.remove(index);
		
		System.out.println("ID : " + getHighestID());
		System.out.println("Spieler : " + current);
		//System.out.println(getHighestID() + " wurde auf " + positions[getHighestID()] + " platziert.");
		System.out.println("0 : " + positions[0]);
		
	}
	

	/**
	 * Introduces the first piece for the black player (which has to be adjacent
	 * to the first (white) piece).
	 * @param pieceType the type of the first black piece
	 * @param hexDirection the relative position of the piece to the first white piece
	 */
	public int introduce( PieceType pieceType, HexDirection hexDirection )throws IllegalArgumentException{
		if((positions[1]==null)&&(PlayerColor.Black==current)){
			
			switch( pieceType ) 
			{
			case GrassHopper:{
				GrassHopper hopper = new GrassHopper(current, 1);
				positions[0].getNeighbor(hexDirection).add(hopper);
				pieces[1]=hopper;
				positions[1]=positions[0].getNeighbor(hexDirection);
				
				break;
				
			}
			case Bug:{
				Bug bugi = new Bug(current, getHighestID()+1);
				positions[0].getNeighbor(hexDirection).add(bugi);
				pieces[1]=bugi;
				positions[1]=positions[0].getNeighbor(hexDirection);
				break;
			}
			case Ant:{
				Ant anti = new Ant(current, getHighestID()+1);
				positions[0].getNeighbor(hexDirection).add(anti);
				pieces[1]=anti;
				positions[1]=positions[0].getNeighbor(hexDirection);
				break;
			}
			case Spider:{
				Spider spidi = new Spider(current, getHighestID()+1);
				positions[0].getNeighbor(hexDirection).add(spidi);
				pieces[1]=spidi;
				positions[1]=positions[0].getNeighbor(hexDirection);
				break;
			}	
			case Queen:{
				if(getHighestID()<2 ||getHighestID()>=8)throw new IllegalArgumentException("Jetzt kann keine Koenigin gelegt werden");
				Queen elisabeth = new Queen(current, getHighestID()+1);
				positions[0].getNeighbor(hexDirection).add(elisabeth);
				pieces[1]=elisabeth;
				positions[1]=positions[0].getNeighbor(hexDirection);
				if(current==PlayerColor.White)
						wqueen=getHighestID()+1;
				bqueen=getHighestID()+1;
			}
			
			}
			for(int i=0;i<blackstorage.size();i++){
				if(blackstorage.get(i)==pieceType){
					blackstorage.remove(i);
					System.out.println("whitestorage : " + whitestorage);
					System.out.println("blackstorage : " + blackstorage);
					break;
				}
			}
			System.out.println("zum testen :" + positions[1]==null );
			return getHighestID()+1;
		}else{
			throw new IllegalArgumentException("Diese Methode nur im zweiten zuge zugelassen.");
		}
		
		
	}

	
	/**
	 * Introduces a new piece to the game board. The piece has to be placed
	 * adjacent to another piece.
	 * @param pieceType the type of the piece
	 * @param anchorPiece the piece specifying the adjacent field
	 * @param direction the direction of the target field (starting from the anchor field)
	 * @return the number of pieces introduced in total
	 */
	public int introduce(PieceType pieceType, int anchorPiece, HexDirection direction)throws IllegalArgumentException {
		if(positions[anchorPiece].isOccupied(direction))throw new IllegalArgumentException("Dieses Feld ist schon besetzt.");
		if (getLeftPiece(current, pieceType)<1)throw new IllegalArgumentException("Alle Steine dieses Typs und dieser Farbe wurden bereits gesetzt");
		if(!containsTuple(anchorPiece, direction))throw new IllegalArgumentException("Spielsteine koennen nur an angrenzende eigene Steine gesetzt werden.");
		if(pieceType!=PieceType.Queen){
			if(current==PlayerColor.Black){
				if((blackstorage.size()==8)&&(bqueen==-1))throw new IllegalArgumentException("Koenigin noch nicht gesetzt.");
			}
			if(current==PlayerColor.White){
				if((whitestorage.size()==8)&&(wqueen==-1))throw new IllegalArgumentException("Koenigin noch nicht gesetzt.");
			}
		}
		switch( pieceType ) {
			case GrassHopper:{
				GrassHopper hopper = new GrassHopper(current, getHighestID()+1);
				positions[anchorPiece].getNeighbor(direction).add(hopper);
				pieces[getHighestID()+1]=hopper;
				positions[getHighestID()+1]=positions[anchorPiece].getNeighbor(direction);
				break;
			}
			case Bug:{
				Bug bugi = new Bug(current, getHighestID()+1);
				positions[anchorPiece].getNeighbor(direction).add(bugi);
				pieces[getHighestID()+1]=bugi;
				positions[getHighestID()+1]=positions[anchorPiece].getNeighbor(direction);
				break;
			}
			case Ant:{
				Ant anti = new Ant(current, getHighestID()+1);
				positions[anchorPiece].getNeighbor(direction).add(anti);
				pieces[getHighestID()+1]=anti;
				positions[getHighestID()+1]=positions[anchorPiece].getNeighbor(direction);
				break;
			}
			case Spider:{
				Spider spidi = new Spider(current, getHighestID()+1);
				positions[anchorPiece].getNeighbor(direction).add(spidi);
				pieces[getHighestID()+1]=spidi;
				positions[getHighestID()+1]=positions[anchorPiece].getNeighbor(direction);
				break;
			}	
			case Queen:{
				if(getHighestID()<1 ||getHighestID()>=7)throw new IllegalArgumentException("Jetzt kann keine Koenigin gelegt werden");
				Queen elisabeth = new Queen(current, getHighestID()+1);
				positions[anchorPiece].getNeighbor(direction).add(elisabeth);
				pieces[getHighestID()+1]=elisabeth;
				positions[getHighestID()+1]=positions[anchorPiece].getNeighbor(direction);
				if(current==PlayerColor.White){
						wqueen=getHighestID()+1;
				}else{
				bqueen=getHighestID()+1;
				}
			}
			
		}
		
		if(current==PlayerColor.White){
			for(int i=0;i<whitestorage.size();i++){
				if(whitestorage.get(i)==pieceType){
					whitestorage.remove(i);
					System.out.println(whitestorage);
					break;
				}
			}
		}else{
			for(int i=0;i<blackstorage.size();i++){
				if(blackstorage.get(i)==pieceType){
					blackstorage.remove(i);
					System.out.println(blackstorage);
					break;
				}
			}
		}
		System.out.println("whitequeen:" + wqueen +"  blackqueen: "+ bqueen );
		
		return getHighestID()+1;
	}

	
	public int getHighestID(){
		return 21-blackstorage.size()-whitestorage.size();	
		
	}
	
	/**
	 * Decides whether the game ended draw. Can only be called when the game is over.
	 * @return {@code true} if the game is over and ended with a draw
	 * @throws IllegalStateException if the game is not ended yet
	 */
	public boolean isDraw() throws IllegalStateException {
		if((isSurrounded(positions[wqueen])) && isSurrounded(positions[bqueen])) return true; 
		return false;
	}

	/**
	 * Decides if the game is over, or not.
	 * @return {@code true} if the game is over
	 */
	public boolean isGameOver() {
		if((wqueen==-1)||(bqueen==-1)) return false;
		if(isSurrounded(positions[wqueen])) return true;
		if(isSurrounded(positions[bqueen])) return true;
		return false;
				
	}

	/**
	 * Moves a piece from one field to another. The target field has to be
	 * adjacent to an existing piece and the position is specified by the
	 * existing piece and a direction (leading to the neighbor).
	 * @param pieceIndex the index of the piece that is moved
	 * @param anchorPiece the piece that is adjacent to the target field
	 * @param direction the direction where the target field lies
	 * @return the number of the piece that already occupies the target field (if any). it is -1 if the field is empty
	 * @throws IllegalStateException if the game is already over
	 * @throws IllegalArgumentException if the target field is occupied and the moved piece does not support stacking
	 */
	public int move(int pieceIndex, int anchorPiece, HexDirection direction) throws IllegalStateException, IllegalArgumentException {
	//	if(queen nicht gesetzt)throw new IllegalArgumentException("");
	//	if(piece das ich bewegen will, nicht oberstes piece ist auf dem feld)IllegalArgumentException("");
	//	if(removalWouldDisconnect(tile)) throw new IllegalArgumentException("");
	//	if(stein noch nicht gesetzt)throw new IllegalArgumentException("");
	//	if(isGameOver())throw new IllegalStateException("Des Spiel ist schon vorbei!");
	//	
	//	GameTile target = positions[anchorPiece].getNeighbor(direction);
	//	int num = getHighestID();
	//	if((target.isOccupied()) && (getPieceType(pieceIndex)!=PieceType.Bug)) return pieceIndex;
		// TODO Auto-generated method stub
		
		return 0;
	}

	// PA 6
	/**
	 * Checks, if the counter can be moved. It is not allowed to move, if the game
	 * would be disconnected during the movement.
	 * @return {@code true} if the counter <b>cannot</b> be moved
	 */
	public boolean removalWouldDisconnect( GameTile tile ) {
			//TODO in PA6	
			return false;
    }
	

	
	public boolean isSurrounded(GameTile tile){
		HexDirection dir = HexDirection.UpperLeft;
		//if((this.wqueen==-1) || (this.bqueen==-1)) return false;
		
		for(int i=0;i<6;i++){
			if(!tile.isOccupied(dir))return false;
			dir=dir.next();
		}
		return true;
	}
	
	
	public void setCurrentPlayer(PlayerColor player){
		this.current=player;
	}
	
	public boolean containsTuple(int n, HexDirection dir){
		for (int i=0; i<getIntroduceable().size(); i++){
			if(getIntroduceable().get(i).getU()==n && getIntroduceable().get(i).getV()==dir)
				return true;
		}
			return false;
		
	}
	


	


	
	
	
	
}
